#ifndef __ASYNCFILELOGGER_H__
#define __ASYNCFILELOGGER_H__

#include "Spark/Utils/NonCopyable.h"
#include "Spark/Utils/Date.h"
#include <memory>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <queue>

namespace wb
{
using StringPtr = std::shared_ptr<std::string>;
using StringPtrQueue = std::queue<StringPtr>;

struct FileName
{
    std::string baseName{"Unnamed"};
    std::string extName{".log"};
    std::string path{"/tmp/"};
    FileName()
    {
    }
    FileName& setBaseName(const std::string& b)
    {
        if (b.length() != 0)
            baseName = b;
        return *this;
    }

    FileName& setExtName(const std::string& e)
    {
        extName = e;
        if (extName.length() == 0)
            extName = ".log";
        if (*extName.begin() != '.')
            extName.insert(extName.begin(), '.');
        return *this;
    }

    FileName& setPath(const std::string& p)
    {
        path = p;
        if (path.length() == 0)
            path = "./";
        if (*path.rbegin() != '/')
            path = path + "/";
        return *this;
    }
    std::string fullName() const
    {
        return path + baseName + extName;
    }
};

class AsyncFileLogger : NonCopyable
{
  public:
    /**
     * @brief Write the message to the log file.
     *
     * @param msg
     * @param len
     */
    void output(const char* msg, const uint64_t len);

    /**
     * @brief Flush data from memory buffer to the log file.
     *
     */
    void flush();

    /**
     * @brief Start writing log files.
     *
     */
    void startLogging();

    /**
     * @brief Set the size limit of log files. When the log file size reaches
     * the limit, the log file is switched.
     *
     * @param limit
     */
    void setFileSizeLimit(uint64_t limit)
    {
        sizeLimit_ = limit;
    }

    void setFileName(FileName file)
    {
        file_ = std::move(file);
    }

    ~AsyncFileLogger();
    AsyncFileLogger();

  protected:
    std::mutex mutex_;
    std::condition_variable cond_;
    StringPtr logBufferPtr_;
    StringPtr nextBufferPtr_;
    StringPtrQueue writeBuffers_;
    StringPtrQueue tmpBuffers_;
    void writeLogToFile(const StringPtr buf);
    std::unique_ptr<std::thread> threadPtr_;
    bool stopFlag_{false};
    void logThreadFunc();
    FileName file_;
    uint64_t sizeLimit_{20 * 1024 * 1024};
    class LoggerFile : NonCopyable
    {
      public:
        LoggerFile(const FileName& f);
        ~LoggerFile();
        void writeLog(const StringPtr buf);
        uint64_t getLength();
        explicit operator bool() const
        {
            return fp_ != nullptr;
        }
        void flush();

      protected:
        FILE* fp_{nullptr};
        TimePoint creationDate_;
        std::string fileFullName_;
        FileName file_;
        static uint64_t fileSeq_;
    };
    std::unique_ptr<LoggerFile> loggerFilePtr_;

    uint64_t lostCounter_{0};
    void swapBuffer();
};

}  // namespace wb

#endif  // __ASYNCFILELOGGER_H__